package com.hoas.api.core;

import com.hoas.api.core.intf.BusinessApiConfig;
import com.hoas.api.core.intf.ApiListener;
import com.hoas.api.core.intf.ApiService;
import com.hoas.api.core.intf.ParamConverter;
import com.hoas.api.core.entity.ApiResult;
import com.hoas.api.core.entity.BusinessUser;
import com.hoas.api.core.entity.UserToken;
import com.hoas.api.core.entity.api.BusinessApi;
import com.hoas.api.core.exception.BusinessException;
import com.hoas.api.encrypt.EncryptUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

import static com.hoas.api.core.entity.ErrorMessage.*;

public class ApiServiceImpl implements ApiService {

	private static Logger logger = LoggerFactory.getLogger(ApiServiceImpl.class);
	
	private BusinessApiConfig apiConfig;
	
	private ParamConverter paramConverter;

	@Override
	public ApiResult callApi(String businessId, String accessToken, String paramStr) throws BusinessException {

		logger.info("call business [{}]", businessId);
		

		if(accessToken == null || "".equals(accessToken)) {
			BusinessException ex = new BusinessException(NO_TOKEN);
			
			failCauseBy(businessId, ex);

		}
		
		BusinessUser user = apiConfig.getUserByAccessToken(accessToken);
		if (user == null) {

			BusinessException ex = new BusinessException(TOKEN_INVALID);
			failCauseBy(businessId, ex);

		}

		if (businessId == null || "".equals(businessId)) {
			
			BusinessException ex = new BusinessException(BUSINESSID_NOTFOUND);
			failCauseBy(businessId, ex);
			
		}
		
		BusinessApi api = apiConfig.getApi(businessId);
		if (api == null) {
			BusinessException ex =  new BusinessException(BUSINESSID_UNKNOWN);
			failCauseBy(businessId, ex);
		}
		
		if(!apiConfig.isAuthorized(user,api)) {
			
			logger.error("user [{}] call business [{}]: 用户未被授权该接口!", user.getUserName(), api.getBusinessId());
			failCauseBy(businessId, new BusinessException(BUSSINESSID_UNAUTH));
		}
		
		Map<String, Object> parameter = null;
		
		logger.info("user [{}] call business [{}],param data[{}]",user.getUserName(),businessId,paramStr);
		
		if (paramStr != null && !"".equals(paramStr)) {

			if (user != null) {
				
				if(user.getEncryptType() != null) {
					
					try {

						paramStr = new EncryptUtil(user.getEncryptType(), null, user.getEncryptKey()).decrypt(paramStr);

					} catch (Exception e) {
						BusinessException ex = new BusinessException(PARAMETER_PARSING_ERROR);
						failCauseBy(businessId, ex);
					}
					
				}
				
				logger.info("user [{}] call business [{}],decrypt param data ==> {}",user.getUserName(),businessId,paramStr);
				
			}

			if(paramConverter != null) {
				parameter = paramConverter.convert(paramStr);
			} else {
				parameter = new HashMap<>();
				parameter.put("_parameter", paramStr);
			}

			if (parameter == null || parameter.size() == 0) {
				BusinessException ex = new BusinessException(PARAMETER_CONVERT_ERROR);
				failCauseBy(businessId, ex);
			}

		}
		
		if(parameter == null) {
			parameter = new HashMap<>();
		}
		
		for(ApiListener listener : apiConfig.getApiListeners()) {
			listener.before(user, api, parameter, null);
		}

		Object result = null;
		try {
			result =  api.process(parameter);

		} catch (BusinessException e) {

			for(ApiListener listener : apiConfig.getApiListeners()) {
				listener.exception(user, api, parameter, e);
			}

			throw e;
		} catch (Exception e) {
			BusinessException ex = new BusinessException(e);

			for(ApiListener listener : apiConfig.getApiListeners()) {
				listener.exception(user, api, parameter, ex);
			}

			throw e;
		}

		for(ApiListener listener : apiConfig.getApiListeners()) {
			listener.after(user, api, parameter, result);
		}
		
		logger.info("user [{}] call business [{}], resp ==> {}",user.getUserName(),businessId,result);
		
		ApiResult apiResult = new ApiResult("0000", "接口请求成功");
		if(result != null) {
			apiResult.setData(result);
		}
		
		return apiResult;
	}
	
	@Override
		public ApiResult getAccessToken(String username, String password) throws BusinessException {
		
		if(username == null || "".equals(username.trim()) || password == null || "".equals(password)) {
			throw new BusinessException(REQUIRED_PARAMETER_NOTFOUND);
		}
		username = username.trim();
		
		BusinessUser user = apiConfig.getUserByUsername(username);
	
		if(user == null) {
			throw new BusinessException(USER_NOTFOUND);
		}
		
		if(!user.getPassword().equals(password)) {
			throw new BusinessException(USER_OR_PASS_ERROR);
		}
		
		UserToken userToken = apiConfig.getToken(user);
		
		ApiResult apiResult = new ApiResult("0000", "接口请求成功");
		Map<String,Object> data = new HashMap<>();
		data.put("accessToken", userToken.getAccessToken());
		data.put("refreshToken", userToken.getRefreshToken());
		data.put("expireTime", userToken.getExpireTime());
		apiResult.setData(data);
		return apiResult;
	}
	
	@Override
	public ApiResult refreshAccessToken(String refreshToken) throws BusinessException {
		
		if(refreshToken == null || "".equals(refreshToken.trim())) {
			throw new BusinessException("9200", "必要参数缺失[refreshToken]");
		}
		
		
		UserToken userToken = apiConfig.refreshToken(refreshToken);
		if (userToken == null) {
			throw new BusinessException("9201", "用户登录信息已失效");
		}
		
		ApiResult apiResult = new ApiResult("0000", "接口请求成功");
		Map<String,Object> data = new HashMap<>();
		data.put("accessToken", userToken.getAccessToken());
		data.put("refreshToken", userToken.getRefreshToken());
		data.put("expireTime", userToken.getExpireTime());
		apiResult.setData(data);
		return apiResult;
	}
	
	private void failCauseBy(String businessId, BusinessException ex) throws BusinessException {
		logger.error("call business [{}] fail, cause by {}:{}", businessId, ex.getCode(), ex.getMessage());
		throw ex;
	}
	
	public void setApiConfig(BusinessApiConfig apiConfig) {
		this.apiConfig = apiConfig;
	}
	
	public BusinessApiConfig getApiConfig() {
		return apiConfig;
	}
	
	public ParamConverter getParamConverter() {
		return paramConverter;
	}
	
	public void setParamConverter(ParamConverter paramConverter) {
		this.paramConverter = paramConverter;
	}

}
